#include "stdafx.h"

#include <vector>

#include "MxUtils\MxError1.h"
#include "..\..\PublicInterfaces\PluginICPopenCV\PluginICPopenCVErrorCodes.h"
#include "PluginICPopenCV.h"

#include "Utils.h"
#include "MakeEllipsePts.h"

#include <opencv2\core\core.hpp>


MakeEllipsePts::MakeEllipsePts() :
	_refCnt(0)
{
}


MakeEllipsePts::~MakeEllipsePts()
{
}

bool		MXSTDMETHOD MakeEllipsePts::Invoke(IImageComputeFn *fn, bool DebugMode, bool DisplayMsg)
{
	bool rc = false;

	if ((fn == nullptr) || (fn->IsValid() == false))
		MX_SETERROR(MX11216, MxError::InvalidParams, MxError::Abort, MxError::VerboseReport, "fn is nullptr, or invalid");
	else
	{
		if (   (fn->IsExistParam("StopProc", IImageComputeFnDef::ParamType::Bool, IImageComputeFnDef::Direction::IO) == false)
			|| (fn->IsExistParam("RootFolder", IImageComputeFnDef::ParamType::String, IImageComputeFnDef::Direction::Input) == false)
			|| (fn->IsExistParam("IJC", IImageComputeFnDef::ParamType::String, IImageComputeFnDef::Direction::Input) == false)
			|| (fn->IsExistParam("IJF", IImageComputeFnDef::ParamType::String, IImageComputeFnDef::Direction::Input) == false)
			|| (fn->IsExistParam("ImageRef", IImageComputeFnDef::ParamType::String, IImageComputeFnDef::Direction::IO) == false)
			|| (fn->IsExistParam("MajorMinorAxis", IImageComputeFnDef::ParamType::String, IImageComputeFnDef::Direction::Input) == false)
			|| (fn->IsExistParam("Delta", IImageComputeFnDef::ParamType::Integer, IImageComputeFnDef::Direction::Input) == false))
			MX_SETERROR(MX11217, MxError::BadUserInput, MxError::Abort, MxError::VerboseReport, "MajorAxis, MinorAxis, PtCnt are not supported by this fn");
		else
		{
			if (fn->GetParamBool("StopProc") == true)
			{
				rc = true;
			}
			else
			{
				std::string result("ArrayInts:[empty]");
				std::string input(fn->GetParamStr("MajorMinorAxis"));

				if (input == result)
					rc = true; 
				else
				{
					if (CreatePoints(fn, fn->GetParamStr("MajorMinorAxis"), fn->GetParamInt("Delta"), &result) == false)
						MX_SETERROR(MX11218, MxError::BadUserInput, MxError::Abort, MxError::VerboseReport, "MajorAxis, MinorAxis, PtCnt are not supported by this fn");
					else
					{
						if (DebugMode == false)
							rc = true;
						else
						{
							std::string imageRefDst("");
							std::string imageRef(fn->GetParamStr("ImageRef"));
							std::string pathfilenameSrcImage("");
							std::string pathfilenameDstImage("");
							std::string pathfilenameDebugImage("");

							if (PluginOpenCV::Utils::GetImagePathFilenames(fn, fn->GetParamStr("RootFolder"), fn->GetParamStr("IJC"), fn->GetParamStr("IJF"), imageRef.c_str(),
								fn->GetFileType(), fn->GetFileType(), //if function changes the filetype (.bmp to .jpg) then add to function params the std IO variable ImageProcFilev1::VariableFileTypeName and set it to new filetype before returning - see Job::Run()
								&imageRefDst, &pathfilenameSrcImage, &pathfilenameDstImage, &pathfilenameDebugImage) == false)
								MX_SETERROR(MX11247, MxError::CodeDefect, MxError::Abort, MxError::VerboseReport, "GetImagePathFilenames failed for step=%d", fn->GetSeqNo());
							else
							{
								if (PluginOpenCV::Utils::WriteDebugImage(fn, pathfilenameSrcImage.c_str(), pathfilenameDstImage.c_str(), pathfilenameDebugImage.c_str(), result.c_str()) == false)
									MX_SETERROR(MX11248, MxError::BadUserInput, MxError::Abort, MxError::VerboseReport, "MajorAxis, MinorAxis, PtCnt are not supported by this fn");
								else
								{
									if (fn->SetParamStr("ImageRef", imageRefDst.c_str()) == false)
										MX_SETERROR(MX11258, MxError::CodeDefect, MxError::Abort, MxError::QuietReport, "SetParamStr(ImageRef, %s) failed for image %s", imageRefDst.c_str(), imageRef.c_str());
									else
									{
										rc = true;
									}
								}
							}
						}
					}
				}
				if (rc == true)
				{
					if (fn->SetParamStr("BoundaryPts", result.c_str()) == false)
					{
						MX_SETERROR(MX11219, MxError::CodeDefect, MxError::Abort, MxError::VerboseReport, "SetParamStr(BoundaryPts) failed");
						rc = false;
					}
				}
			}
		}
	}
	return rc;
}


bool		MXSTDMETHOD MakeEllipsePts::CreatePoints(IImageComputeFn *fn, const char *ArrayIntMajorMinorPoints, int Delta, std::string *ArrayIntResultPoints)
{
	bool rc = false;

	if ((ArrayIntMajorMinorPoints == nullptr) || (ArrayIntResultPoints == nullptr) || (Delta < 5)  )
		MX_SETERROR(MX11220, MxError::InvalidParams, MxError::Abort, MxError::VerboseReport, "one or more params is invalid");
	else
	{	
		int		*arrayIntMajorMinor = nullptr;
		int		*resultArray = nullptr;
		char    *resultBuff = nullptr;

		try
		{
			int arrayCnt = 0;
			if (((arrayCnt = fn->ArrayIntsGetSize(ArrayIntMajorMinorPoints)) == ArrayInts::Invalid) || ((arrayCnt != 4) && (arrayCnt != 8)))
				MX_SETERROR(MX11223, MxError::BadUserInput, MxError::Abort, MxError::VerboseReport, "ArrayInts::GetSize(ArrayIntMajorPoints) failed or returned %d rather than 4", arrayCnt);
			else
			{
				arrayIntMajorMinor = new int[8];
				if (arrayIntMajorMinor == nullptr) 
					MX_SETERROR(MX11224, MxError::System, MxError::Abort, MxError::QuietReport, "arrayIntMajor == nullptr or arrayIntMinor == nullptr - out of memory?");
				else
				{
					if (fn->ArrayIntsConvertStr2Ints(ArrayIntMajorMinorPoints, arrayIntMajorMinor, arrayCnt, IImageComputeFn::Invalid) == false)
						MX_SETERROR(MX11225, MxError::CodeDefect, MxError::Abort, MxError::QuietReport, "ArrayInts::ConvertStr2Ints(ArrayIntMajorMinorPoints) failed");
					else
					{
						CvPoint centre;
						int majorLen = 0;
						int minorLen = 0;
						int angle = 0;
						if (fn->CalcEllipseParams(arrayIntMajorMinor[0], arrayIntMajorMinor[1], arrayIntMajorMinor[2], arrayIntMajorMinor[3], 
							(arrayCnt == 8) ? arrayIntMajorMinor[4] : arrayIntMajorMinor[0], 
							(arrayCnt == 8) ? arrayIntMajorMinor[5] : arrayIntMajorMinor[1], 
							(arrayCnt == 8) ? arrayIntMajorMinor[6] : arrayIntMajorMinor[2], 
							(arrayCnt == 8) ? arrayIntMajorMinor[7] : arrayIntMajorMinor[3],
							&centre.x, &centre.y, &majorLen, &minorLen, &angle) == false)
							MX_SETERROR(MX11229, MxError::CodeDefect, MxError::Abort, MxError::QuietReport, "CalcEllipseParams() failed");
						else
						{
							cv::vector<cv::Point> pts((360 / Delta) + 1);
							cv::ellipse2Poly(centre, cv::Size(majorLen / 2, minorLen / 2), angle, 0, 360, Delta, pts);

							if ((resultArray = new int[pts.size() * 2]) == nullptr)
								MX_SETERROR(MX11230, MxError::System, MxError::Abort, MxError::QuietReport, "resultArray == nullptr for size=%d - out of memory?", pts.size()*2);
							else
							{
								size_t y = 0;
								for (size_t x = 0; x < pts.size(); x++)
								{
									resultArray[y] = pts[x].x;
									resultArray[y+1] = pts[x].y;
									y += 2;
								}
								int resultBuffLen = 0;
								if ((resultBuffLen = fn->ArrayIntsGetStrLength(resultArray, pts.size() * 2, IImageComputeFn::Invalid)) == IImageComputeFn::Invalid)
									MX_SETERROR(MX11231, MxError::CodeDefect, MxError::Abort, MxError::QuietReport, "ArrayIntsGetStrLengt() failed for  array size=%d", pts.size()*2);
								else
								{
									if ((resultBuff = new char[resultBuffLen + 1]) == nullptr)
										MX_SETERROR(MX11232, MxError::System, MxError::Abort, MxError::QuietReport, "resultBuff == nullptr for resultBuffLen=%d - out of memory?", resultBuffLen);
									else
									{
										if (fn->ArrayIntsConvertInts2Str(resultArray, pts.size() * 2, resultBuff, resultBuffLen, IImageComputeFn::Invalid) == false)
											MX_SETERROR(MX11233, MxError::CodeDefect, MxError::Abort, MxError::QuietReport, "ArrayIntsConvertInts2Str() failed");
										else
										{
											ArrayIntResultPoints->assign(resultBuff);
											rc = true;
										}
									}
								}
							}
						}
					}
				}
			}
		}
		catch (std::exception& e)
		{
			std::string result("[exception msg not found]");
			MX_SETERROR(MX11221, MxError::CodeDefect, MxError::Abort, MxError::QuietReport, "threw exception %s", PluginICPopenCV::GetOpenCVExceptionMsg(e, &result));
		}
		if (arrayIntMajorMinor != nullptr)
			delete[] arrayIntMajorMinor;
		if (resultArray != nullptr)
			delete[] resultArray;
		if (resultBuff != nullptr)
			delete[] resultBuff;
	}
	return rc;
}


long		MXSTDMETHOD MakeEllipsePts::DestroyPtr(void)
{
	long cnt = InterlockedDecrement(&_refCnt);
	if (cnt == 0)
		delete this;
	return cnt;
}

long		MXSTDMETHOD MakeEllipsePts::DuplicatePtr(void)
{
	return InterlockedIncrement(&_refCnt);
}

void *		MXSTDMETHOD MakeEllipsePts::Dynamic_cast(const GUID IId)
{
	void *rc = nullptr;

	if (MxGUID::IsEqual(IId, IMxPluginBaseIID::Guid()))
		rc = static_cast<IMxPluginBase *>(this);
	else
		rc = nullptr;

	std::string tmp;
	if (rc == nullptr)
		MX_SETERROR(MX11222, MxError::Install, MxError::Abort, MxError::QuietReport, "Dynamic_cast does not support IID=%s", MxGUID::ConvertToString(IId, &tmp));
	else
		((IMxPluginBase *)rc)->DuplicatePtr();

	return rc;
}
